home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Developer Essentials / DTS Sample Code / System 7.0 Samples / ProcDoggie 1.0a6 / UProcessGuts.inc1.p < prev    next >
Encoding:
Text File  |  1991-02-07  |  68.1 KB  |  1,925 lines  |  [TEXT/MPS ]

  1. {-------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    ProcDoggie-specific Process Manager code
  6. #
  7. #    Program:    ProcDoggie
  8. #    File:        UProcessGuts.inc1.p - Pascal Implementation
  9. #
  10. #    by:        Forrest Tanaka
  11. #
  12. #    Copyright © 1988-1991 Apple Computer, Inc.
  13. #    All rights reserved.
  14. #
  15. -------------------------------------------------------------------------------}
  16. {[j=20/57/1$] Pasmat Options}
  17. {$R-}
  18.  
  19.  
  20. (*******************************************************************************
  21. * Constants
  22. *******************************************************************************)
  23.  
  24.     CONST
  25.         rProcessListWindID = 128; {Resource ID of process list window template}
  26.         rProcessInfoWindID = 129; {Resource ID of process info window template}
  27.         rProcessInfoDitlID = 129; {Resource ID of process info dialog item list}
  28.  
  29.         kProcessListWindKind = 8;    {In windowKind field of process list windows}
  30.         kProcessInfoWindKind = 9;    {In windowKind field of process info windows}
  31.         kActivateList        = TRUE; {Pass to LActivate to specify activate list}
  32.         kScrollBarWidth      = 16;   {Width of scroll bar in pixels}
  33.  
  34.         rAppOrDAStringID = 128; {Resource ID of Application or DA string}
  35.         kAppStringInd    = 1;   {Index for Application string}
  36.         kDAStringInd     = 2;   {Index for Desk Accessory string}
  37.  
  38.         rCheckMarkID = 128; {Resource ID of checkmark string}
  39.  
  40.         kProcessNameItem   = 1;  {Dialog item # of process name}
  41.         kAppOrDAItem       = 2;  {Dialog item # of Application/DA string}
  42.         kTotalSizeItem     = 5;  {Dialog item # of Total Size readout}
  43.         kFreeSpaceItem     = 6;  {Dialog item # of Free Space readout}
  44.         kMemIndicatorItem  = 7;  {Dialog item # of partition memory indicator}
  45.         kGrayLineItem0     = 8;  {Dialog item # of first gray line}
  46.         kTypeItem          = 11; {Dialog item # of TYPE item}
  47.         kCreatorItem       = 12; {Dialog item # of Creator item}
  48.         kGrayLineItem1     = 13; {Dialog item # of second gray line}
  49.         kSusResChkItem     = 14; {Dialog item # of suspend/resume checkmark}
  50.         kWindActChkItem    = 15; {Dialog item # of window activate checkmark}
  51.         kGetClickChkItem   = 16; {Dialog item # of Get front click checkmark}
  52.         kAppDiedChkItem    = 17; {Dialog item # of App Died checkmark}
  53.         kStationeryChkItem = 18; {Dialog item # of Stationery checkmark}
  54.         kCanBackChkItem    = 19; {Dialog item # of Can Background checkmark}
  55.         kOnlyBackChkItem   = 20; {Dialog item # of Only Background checkmark}
  56.         kHighLevelChkItem  = 21; {Dialog item # of High-Level Evt checkmark}
  57.         kRHighLevelChkItem = 22; {Dialog item # of Remote High-Level checkmark}
  58.         kMultiUserChkItem  = 23; {Dialog item # of Multi-user Launch checkmark}
  59.         k32BitCleanChkItem = 24; {Dialog item # of 32-Bit Clean checkmark}
  60.  
  61.         kUsedColor = 2; {Process Info window palette color for used memory}
  62.         kFreeColor = 3; {Process Info window palette color for free memory}
  63.  
  64.  
  65. (*******************************************************************************
  66. * Types
  67. *******************************************************************************)
  68.  
  69.     TYPE
  70.         {Pointer to canonical format for number strings}
  71.         NumFormatStringPtr = ^NumFormatString;
  72.  
  73.         {Pointer to process serial number}
  74.         PSNPtr = ^ProcessSerialNumber;
  75.  
  76.  
  77. (*******************************************************************************
  78. * Variables
  79. *******************************************************************************)
  80.  
  81.     VAR
  82.         gLaunchMode: LaunchModeCode; {Open documents? Print documents?}
  83.  
  84.  
  85. {$S ProcessGuts}
  86. (*******************************************************************************
  87. * Public: SetLaunchMode
  88. *
  89. * The global variable, "gLaunchMode", is set to the launch mode specified by
  90. * "newMode".
  91. *******************************************************************************)
  92.  
  93.     PROCEDURE SetLaunchMode (newMode: LaunchModeCode);
  94.  
  95.     BEGIN
  96.         gLaunchMode := newMode
  97.     END;
  98.  
  99.  
  100. {$S ProcessGuts}
  101. (*******************************************************************************
  102. * Public: GetLaunchMode
  103. *
  104. * The value of the global variable, "gLaunchMode", is returned.
  105. *******************************************************************************)
  106.  
  107.     FUNCTION GetLaunchMode: LaunchModeCode;
  108.  
  109.     BEGIN
  110.         GetLaunchMode := gLaunchMode
  111.     END;
  112.  
  113.  
  114. {$S Main}
  115. (*******************************************************************************
  116. * Public: IsProcessListWindow
  117. *
  118. * I store a unique code in the windowKind field of every window I create so that
  119. * I can identify the kind of window it is later… like now!  I check to see if
  120. * the windowKind field of aWindow is kProcessListWindKind or not.  If it is, I
  121. * know it’s a process list window, and so IsProcessListWindow returns TRUE.
  122. *******************************************************************************)
  123.  
  124.     FUNCTION IsProcessListWindow (aWindow: WindowPtr): Boolean;
  125.  
  126.     BEGIN
  127.         IF aWindow <> NIL THEN
  128.             IsProcessListWindow := WindowPeek(aWindow)^.windowKind =
  129.                     kProcessListWindKind
  130.         ELSE
  131.             IsProcessListWindow := FALSE
  132.     END;
  133.  
  134.  
  135. {$S ProcessGuts}
  136. (*******************************************************************************
  137. * Public: CreateProcessListWindow
  138. *
  139. * I store the constant kProcessListWindKind into the windowKind field of the new
  140. * window.  When the routine IsProcessListWindow is called, it uses this field to
  141. * identify a window as a process list window.
  142. *
  143. * See the UWindowHandler unit for code to create a new window.
  144. *******************************************************************************)
  145.  
  146.     FUNCTION CreateProcessListWindow: WindowPtr;
  147.  
  148.         CONST
  149.             kDrawList      = TRUE; {Pass to LNew; list must be drawn immediately}
  150.             kHasGrow       = TRUE; {Pass to LNew; list has grow box}
  151.             kHasHorzScroll = TRUE; {Pass to LNew; list has a horizontal scroll bar}
  152.             kHasVertScroll = TRUE; {Pass to LNew; list has a vertical scroll bar}
  153.  
  154.         VAR
  155.             aWindow:        WindowPtr;  {Pointer to the process list window}
  156.             processList:    ListHandle; {Handle to the list of processes}
  157.             listRect:       Rect;       {Rectangle of list in window coords}
  158.             listDimensions: Rect;       {Dimensions of list in cells}
  159.             cellSize:       Point;      {Size of cell in pixels}
  160.             currFont:       FontInfo;   {Information about current port’s font}
  161.  
  162.         PROCEDURE HandleError (messageClass: Integer;
  163.                                messageIndex: Integer);
  164.  
  165.             VAR
  166.                 result: Integer; {Result of alert; ignored}
  167.  
  168.         BEGIN
  169.             IF aWindow <> NIL THEN
  170.                 BEGIN
  171.                     CloseWindow (aWindow);
  172.                     DisposPtr (Ptr(aWindow))
  173.                 END;
  174.             result := ShowStopAlert (messageClass, messageIndex);
  175.             gError := noErr;
  176.             CreateProcessListWindow := NIL;
  177.             EXIT (CreateProcessListWindow)
  178.         END;
  179.  
  180.     BEGIN
  181.         aWindow := NIL;
  182.  
  183.         (* Create the new window *)
  184.         aWindow := CreateWindow (rProcessListWindID);
  185.         IF gError <> noErr THEN
  186.             IF gError = memFullErr THEN
  187.                 HandleError (rMemErrMessages, kMemErrProcListOpenMsg)
  188.             ELSE IF gError = resNotFound THEN
  189.                 HandleError (rResErrMessages, kResErrAppDamageMsg)
  190.             ELSE IF gError = dsSysErr THEN
  191.                 HandleError (rMiscErrMessages, kMiscErrUnknownMsg);
  192.  
  193.         (* Set up the window *)
  194.         SetPort (aWindow);
  195.         WindowPeek(aWindow)^.windowKind := kProcessListWindKind;
  196.         TextFont (1);
  197.  
  198.         (* Create the process list *)
  199.         GetFontInfo ((*<*)currFont);
  200.         listRect := aWindow^.portRect;
  201.         listRect.right := listRect.right - kScrollBarWidth + 1;
  202.         SetRect ((*<*)listDimensions, 0, 0, 1, 0);
  203.         cellSize.h := listRect.right - listRect.left;
  204.         cellSize.v := currFont.ascent + currFont.descent + currFont.leading;
  205.         processList := LNew (listRect, listDimensions, cellSize, 128, aWindow,
  206.                 kDrawList, NOT kHasGrow, NOT kHasHorzScroll, kHasVertScroll);
  207.         IF FailLowMemory (0) THEN
  208.             HandleError (rMemErrMessages, kMemErrProcListOpenMsg);
  209.  
  210.         (* Make sure the list is activated *)
  211.         LActivate (kActivateList, processList);
  212.  
  213.         (* Save a handle to the list in the refCon of the window *)
  214.         SetWRefCon (aWindow, LongInt (processList));
  215.  
  216.         (* Set the new window as the current GrafPort and return *)
  217.         SetPort (aWindow);
  218.         CreateProcessListWindow := aWindow
  219.     END;
  220.  
  221.  
  222. {$S ProcessGuts}
  223. (*******************************************************************************
  224. * Private: EqualPSN - List Manager search proc
  225. *
  226. * The List Manager’s LSearch function can take a pointer to a routine that
  227. * checks to see if a record matches an entry in the list.  The routine must have
  228. * an interface identical to IUMagIDString.  EqualPSN is the routine that I pass
  229. * to LSearch in the IdleProcessListWindow routine.  It compares the process
  230. * serial number passed in testPSN against the process serial number contained in
  231. * the ProcessListInfoRec of a cell.  Because I already know the lengths of
  232. * ProcessListInfoRecs and ProcessSerialNumber records, I ignore the aLen and
  233. * bLen parameters.
  234. *
  235. * If the two process serial numbers refer to the same process, then EqualPSN
  236. * returns 0, otherwise it returns 1.
  237. *******************************************************************************)
  238.  
  239.     FUNCTION EqualPSN (processInfo: ProcessListInfoPtr;
  240.                        testPSN:     ProcessSerialNumberPtr;
  241.                        aLen:        Integer;
  242.                        bLen:        Integer): Integer;
  243.  
  244.         VAR
  245.             equal: Boolean; {TRUE if PSNs are equal}
  246.             error: OSErr;
  247.  
  248.     BEGIN
  249.         error := SameProcess (testPSN^, processInfo^.serialNumber, (*<*)equal);
  250.         IF equal THEN
  251.             EqualPSN := 0
  252.         ELSE
  253.             EqualPSN := 1
  254.     END;
  255.  
  256.  
  257. {$S ProcessGuts}
  258. (*******************************************************************************
  259. * Private: SetProcessListInfo - Set process list cell info
  260. *
  261. * SetProcessListInfo sets the cell specified by row (I’m only using one column,
  262. * so only the row matters) of the list specified by procList to the information
  263. * in procInfo.  My lists contain ProcessListInfoRecs, which contain only two of
  264. * the fields in ProcessInfoRecs (process name and process serial number), so I
  265. * just copy these two fields from procInfo into listInfo.  I then use LSetCell
  266. * to copy listInfo into the list.
  267. *******************************************************************************)
  268.  
  269.     PROCEDURE SetProcessListInfo (procInfo: ProcessInfoRec;
  270.                                   row:      Integer;
  271.                                   procList: ListHandle);
  272.  
  273.         VAR
  274.             listInfo: ProcessListInfoRec; {Process info from List Mgr list}
  275.             newCell:  Cell;               {Cell in which to set information}
  276.             result:   Integer;            {Result of alert; ignored}
  277.  
  278.     BEGIN
  279.         (* Copy the process name *)
  280.         BlockMove (Ptr(procInfo.processName), @listInfo.processName,
  281.                 ORD (procInfo.processName^ [0]) + 1);
  282.  
  283.         (* Copy the process serial number *)
  284.         listInfo.serialNumber := procInfo.processNumber;
  285.  
  286.         (* Set the specified cell to the new ProcessListInfoRec *)
  287.         newCell.h := 0;
  288.         newCell.v := row;
  289.         LSetCell (Ptr(@listInfo), SIZEOF (ProcessListInfoRec), newCell, procList)
  290.     END;
  291.  
  292.  
  293. {$S ProcessGuts}
  294. (*******************************************************************************
  295. * Public: IdleProcessListWindow
  296. *
  297. * I’m using a simple algorithm to keep the process list window’s process list
  298. * updated to the Process Manager’s process list, but it’ll probably be tough to
  299. * describe.  Here goes. . .
  300. *
  301. * I compare the process serial number of each entry in the Process Manager’s
  302. * list against the process serial number of the corresponding entry in
  303. * the process list window’s list.  If they match, then I just go on to the next
  304. * entries of the lists.  If they don’t match, then I search the window’s list in
  305. * case the matching process is farther down.  If I do find it farther down, then
  306. * I assume that the processes in the window’s list that come between the
  307. * matching entries in the two lists were deleted.  So, I delete those rows.  If
  308. * I don’t find it farther down, then I assume that the entry is new.  I then
  309. * insert a new row in the corresponding position of the window’s list and copy
  310. * the process information to it.
  311. *
  312. * If I run out of rows in the window’s list before getting through the entire
  313. * Process Manager list, then I just keep adding new rows to the end of the
  314. * window’s list and copying over the balance.
  315. *
  316. * If I go through the entire Process Manager list but there are left-over
  317. * entries in the window’s list, then I just delete those left-overs.
  318. *
  319. * So, that’s the algorithm.  It was the most efficient one I could come up with
  320. * that wasn’t even harder to explain.  Beware: some parts of this routine have
  321. * only gotten minimal testing, so I wouldn’t be surprised if you find bugs.
  322. *******************************************************************************)
  323.  
  324.     PROCEDURE IdleProcessListWindow (processListWindow: WindowPtr);
  325.  
  326.         VAR
  327.             procNum:        ProcessSerialNumber; {Serial number of open processes}
  328.             procInfo:       ProcessInfoRec;      {Process info from Proc Mgr list}
  329.             procName:       Str31;               {Name of the process}
  330.             listInfo:       ProcessListInfoRec;  {Process info from List Mgr list}
  331.             listInfoLength: Integer;             {Size of ProcessListInfoRec}
  332.             currCell:       Cell;                {List cell being checked}
  333.             matchCell:      Cell;                {Cell with matching PSN}
  334.             procList:       ListHandle;          {Handle to List Mgr process list}
  335.             foundMatch:     Boolean;             {Found matching List Mgr entry}
  336.             equal:          Boolean;             {Proc and List Mgr elements match}
  337.             result:         Integer;             {Result of alert; ignored}
  338.             addedProcess:   Boolean;             {TRUE if a process added to list}
  339.             error:          OSErr;
  340.  
  341.     BEGIN
  342.         (* Get the List Manager’s copy of the process list *)
  343.         procList := ListHandle(GetWRefCon (processListWindow));
  344.  
  345.         (* Start checking from start of List Mgr and Process Mgr lists *)
  346.         addedProcess := FALSE;
  347.         currCell.v := 0;
  348.         currCell.h := 0;
  349.         procNum.highLongOfPSN := 0;
  350.         procNum.lowLongOfPSN := kNoProcess;
  351.  
  352.         (* Keep looping through each open process *)
  353.         WHILE GetNextProcess ((*◊*)procNum) = noErr DO
  354.             BEGIN
  355.                 (* Get information about an open process *)
  356.                 procInfo.processInfoLength := SIZEOF (ProcessInfoRec);
  357.                 procInfo.processName := @procName;
  358.                 procInfo.processAppSpec := NIL;
  359.                 error := GetProcessInformation (procNum, (*◊*)procInfo);
  360.  
  361.                 (* Cmp List Mgr & Proc Mgr lists if enuf cells for # of processes *)
  362.                 IF PtInRect (currCell, procList^^.dataBounds) THEN
  363.                     BEGIN
  364.                         (* Get process info from List Mgr list *)
  365.                         listInfoLength := SIZEOF (ProcessListInfoRec);
  366.                         LGetCell ((*<*)@listInfo, (*◊*)listInfoLength, currCell,
  367.                                 procList);
  368.  
  369.                         (* If Proc & List Mgr lists differ, update List Mgr list *)
  370.                         error := SameProcess (procInfo.processNumber, listInfo.
  371.                                 serialNumber, (*<*)equal);
  372.                         IF NOT equal THEN
  373.                             BEGIN
  374.                                 (* See if matching process farther down List Mgr list *)
  375.                                 matchCell := currCell;
  376.                                 foundMatch := LSearch (@procInfo.processNumber,
  377.                                         SIZEOF (ProcessSerialNumber), @EqualPSN,
  378.                                         (*◊*)matchCell, procList);
  379.  
  380.                                 (* Was there a match farther down the List Mgr list? *)
  381.                                 IF foundMatch THEN
  382.                                     (* Yes, delete intervening cells *)
  383.                                     LDelRow (matchCell.v - currCell.v, currCell.v,
  384.                                             procList)
  385.                                 ELSE
  386.                                     (* No, insert the new process into List Mgr list *)
  387.                                     BEGIN
  388.                                         currCell.v := LAddRow (1, currCell.v, procList);
  389.                                         SetProcessListInfo (procInfo, currCell.v,
  390.                                                 procList)
  391.                                     END
  392.                             END
  393.                     END
  394.                 ELSE
  395.                     BEGIN
  396.                         (* Ran out of rows, add one *)
  397.                         currCell.v := LAddRow (1, currCell.v, procList);
  398.                         addedProcess := TRUE;
  399.  
  400.                         (* Set the new row to the new process information *)
  401.                         SetProcessListInfo (procInfo, currCell.v, procList)
  402.                     END;
  403.  
  404.                 (* Go to the next cell element in List Mgr list *)
  405.                 currCell.v := SUCC (currCell.v)
  406.             END;
  407.  
  408.         (* Delete any extraneous cells *)
  409.         IF currCell.v < procList^^.dataBounds.bottom THEN
  410.             LDelRow (procList^^.dataBounds.bottom - currCell.v, currCell.v,
  411.                     procList);
  412.  
  413.         (* If added processes to the list and memory low, warn *)
  414.         IF addedProcess AND FailLowMemory (0) THEN
  415.             result := ShowCautionOKAlert (rMemErrMessages, kMemErrLowMemWarnMsg)
  416.     END;
  417.  
  418.  
  419. {$S ProcessGuts}
  420. (*******************************************************************************
  421. * Public: DrawProcessListWindow
  422. *
  423. * Not much here to explain.
  424. *******************************************************************************)
  425.  
  426.     PROCEDURE DrawProcessListWindow (processListWindow: WindowPtr);
  427.  
  428.         VAR
  429.             procList: ListHandle; {Handle to List Mgr process list}
  430.  
  431.     BEGIN
  432.         SetPort (processListWindow);
  433.  
  434.         (* Get the List Manager’s copy of the process list *)
  435.         procList := ListHandle(GetWRefCon (processListWindow));
  436.  
  437.         (* Update the list *)
  438.         TextFont (1);
  439.         TextFace ([]);
  440.         TextSize (GetDefFontSize);
  441.         EraseRect (processListWindow^.portRect);
  442.         LUpdate (processListWindow^.visRgn, procList);
  443.     END;
  444.  
  445.  
  446. {$S ProcessGuts}
  447. (*******************************************************************************
  448. * Public: ClickProcessListWindow
  449. *
  450. * The List Manager is doing the lion’s share of the work.
  451. *******************************************************************************)
  452.  
  453.     PROCEDURE ClickProcessListWindow (processListWindow: WindowPtr;
  454.                                       clickEvent:        EventRecord);
  455.  
  456.         VAR
  457.             procList:    ListHandle; {Handle to List Mgr process list}
  458.             clickPos:    Point;      {Position of mouse click in window coords}
  459.             doubleClick: Boolean;    {TRUE if cell was double-clicked}
  460.  
  461.     BEGIN
  462.         SetPort (processListWindow);
  463.  
  464.         (* Get the List Manager’s copy of the process list *)
  465.         procList := ListHandle(GetWRefCon (processListWindow));
  466.  
  467.         (* Call the List Manager to handle the click *)
  468.         clickPos := clickEvent.where;
  469.         GlobalToLocal ((*◊*)clickPos);
  470.         doubleClick := LClick (clickPos, clickEvent.modifiers, procList);
  471.         IF doubleClick THEN
  472.             DoBringProcessToFront (processListWindow);
  473.     END;
  474.  
  475.  
  476. {$S ProcessGuts}
  477. (*******************************************************************************
  478. * Public: ActivateProcessListWindow
  479. *
  480. * The List Manager is called to activate/deactivate the process list window.
  481. *******************************************************************************)
  482.  
  483.     PROCEDURE ActivateProcessListWindow (processListWindow: WindowPtr;
  484.                                          becomingActive:    Boolean);
  485.  
  486.         VAR
  487.             procList: ListHandle; {Handle to List Mgr process list}
  488.  
  489.     BEGIN
  490.         SetPort (processListWindow);
  491.  
  492.         (* Get the List Manager’s copy of the process list *)
  493.         procList := ListHandle(GetWRefCon (processListWindow));
  494.  
  495.         (* Call the List Manager to activate or deactivate the list *)
  496.         LActivate (becomingActive, procList)
  497.     END;
  498.  
  499.  
  500. {$S ProcessGuts}
  501. (*******************************************************************************
  502. * Public: FixProcessListMenus
  503. *
  504. * The three launching items in the File menu are enabled as long as there’s
  505. * enough memory available.
  506. *
  507. * The List Manager routine, LGetSelect, is called to see if there are any
  508. * processes in the Process List window specified by the "processListWindow"
  509. * parameter that are selected.  If there are, then the three items in the
  510. * Process menu are enabled.  If there isn’t enough memory to safely work in,
  511. * then only the Bring Process to Front is enabled.
  512. *******************************************************************************)
  513.  
  514.     PROCEDURE FixProcessListMenus (processListWindow: WindowPtr);
  515.  
  516.         CONST
  517.             kFindNext = TRUE; {Pass to LGetSelect to find sequence of selections}
  518.  
  519.         VAR
  520.             aMenu:    MenuHandle; {Handle to any menu we’re checking on}
  521.             procList: ListHandle; {Handle to List Mgr process list}
  522.             aCell:    Cell;       {Cell of process list}
  523.  
  524.     BEGIN
  525.         (* Get the List Manager’s copy of the process list *)
  526.         procList := ListHandle(GetWRefCon (processListWindow));
  527.  
  528.         (* Enable the File menu launch items *)
  529.         aMenu := GetMHandle (mFile);
  530.         IF NOT FailLowMemory (0) THEN
  531.             BEGIN
  532.                 EnableItem (aMenu, iLaunchFore);
  533.                 EnableItem (aMenu, iLaunchBack);
  534.                 EnableItem (aMenu, iLaunchTo)
  535.             END;
  536.  
  537.         (* Undim the Process menu items *)
  538.         aMenu := GetMHandle (mProcess);
  539.         aCell.v := 0;
  540.         aCell.h := 0;
  541.         IF LGetSelect (kFindNext, (*◊*)aCell, procList) THEN
  542.             BEGIN
  543.                 (* There’s ≥ 1 sel’d process, enable Bring Process to Front *)
  544.                 EnableItem (aMenu, iBringFront);
  545.  
  546.                 (* Only enable other two items if enough memory to safely work *)
  547.                 IF NOT FailLowMemory (0) THEN
  548.                     BEGIN
  549.                         EnableItem (aMenu, iShowProcessInfo);
  550.                         EnableItem (aMenu, iTerminateProcess)
  551.                     END
  552.             END
  553.     END;
  554.  
  555.  
  556. {$S Main}
  557. (*******************************************************************************
  558. * Public: IsProcessInfoWindow
  559. *
  560. * I store a unique code in the windowKind field of every window I create so that
  561. * I can identify the kind of window it is later… like now!  I check to see if
  562. * the windowKind field of aWindow is kProcessInfoWindKind or not.  If it is, I
  563. * know it’s a process info window, and so IsProcessInfoWindow returns TRUE.
  564. *******************************************************************************)
  565.  
  566.     FUNCTION IsProcessInfoWindow (aWindow: WindowPtr): Boolean;
  567.  
  568.     BEGIN
  569.         IF aWindow <> NIL THEN
  570.             IsProcessInfoWindow := WindowPeek(aWindow)^.windowKind =
  571.                     kProcessInfoWindKind
  572.         ELSE
  573.             IsProcessInfoWindow := FALSE
  574.     END;
  575.  
  576.  
  577. {$S ProcessGuts}
  578. (*******************************************************************************
  579. * Private: GetNumberParts - Get the default number parts table
  580. *
  581. * To use the Script Manager’s number conversion routines, the number parts table
  582. * in the 'itl4' resource must be retrieved.  This routine gets the itl4 resource
  583. * and copies the number parts table into the "partsTable" parameter.
  584. *
  585. * If the retrieval was successful, then TRUE is returned.  If the itl4 resource
  586. * couldn’t be loaded for some reason, then FALSE is returned.
  587. *******************************************************************************)
  588.  
  589.     FUNCTION GetNumberParts (VAR partsTable: NumberParts): Boolean;
  590.  
  591.         VAR
  592.             intl4: Itl4Handle; {Handle to the itl4 resource}
  593.  
  594.     BEGIN
  595.         intl4 := Itl4Handle(IUGetIntl (4));
  596.         IF intl4 <> NIL THEN
  597.             BEGIN
  598.                 partsTable := NumberPartsPtr(ORD (intl4^) + intl4^^.
  599.                         defPartsOffset)^;
  600.                 GetNumberParts := TRUE
  601.             END
  602.         ELSE
  603.             GetNumberParts := FALSE
  604.     END;
  605.  
  606.  
  607. {$S ProcessGuts}
  608. (*******************************************************************************
  609. * Private: TextLineBox - Draw a line of text into a box.
  610. *
  611. * This routine is very similar to TextEdit’s TextBox routine, and in fact it
  612. * takes the same parameters.  But TextLineBox draws a single line of text
  613. * specified by "textLine" and having the length specified by "length" into
  614. * the current GrafPort, ignoring carriage returns and word-wrap.  This means
  615. * that there’s less overhead than TextBox.  But TextBox itself is optimized for
  616. * single lines of text, so there is an ulterior motive for this routine.
  617. * TextBox erases the entire box before drawing the text.  This results in a
  618. * slight flicker if TextBox is called to draw over previous text.  TextLineBox
  619. * only erases the part of the box that isn’t covered with the text specified by
  620. * "textLine".  Also, the text is drawn in srcCopy mode.  If TextLineBox is
  621. * called to draw over existing text, the result should be a smooth transition
  622. * from one text to another, without flicker.
  623. *******************************************************************************)
  624.  
  625.     PROCEDURE TextLineBox (textLine: Ptr;
  626.                            length:   Integer;
  627.                            box:      Rect;
  628.                            just:     Integer);
  629.  
  630.         VAR
  631.             currPort:     GrafPtr;   {Pointer to the current GrafPort}
  632.             currTextMode: Integer;   {Current text mode}
  633.             currFont:     FontInfo;  {Current font information}
  634.             lineWidth:    Integer;   {Width of line of text in pixels}
  635.             spareSpace:   Integer;   {Width of box - width of text}
  636.             spareRect:    Rect;      {Rectangle of area not filled with text}
  637.             currClip:     RgnHandle; {Handle to the current clip region}
  638.  
  639.     BEGIN
  640.         (* Save the current clip region and set the clip region to "box" *)
  641.         currClip := NewRgn;
  642.         GetClip ((*<*)currClip);
  643.         ClipRect (box);
  644.  
  645.         (* Save the current text mode and set it to srcCopy *)
  646.         GetPort ((*<*)currPort);
  647.         currTextMode := currPort^.txMode;
  648.         TextMode (srcCopy);
  649.  
  650.         (* If default justification, set to real justification based on SysJust *)
  651.         IF just = teFlushDefault THEN
  652.             IF GetSysJust = 0 THEN
  653.                 just := teFlushLeft
  654.             ELSE
  655.                 just := teFlushRight;
  656.  
  657.         (* Move pen to baseline on left side of box *)
  658.         GetFontInfo (currFont);
  659.         MoveTo (box.left, box.top + currFont.ascent);
  660.  
  661.         (* Find the width of the specified text *)
  662.         lineWidth := TextWidth (textLine, 0, length);
  663.  
  664.         (* Adjust the pen for centered or right-aligned text *)
  665.         IF just <> teFlushLeft THEN
  666.             BEGIN
  667.                 spareSpace := box.right - box.left - lineWidth;
  668.                 IF just = teCenter THEN
  669.                     spareSpace := spareSpace DIV 2;
  670.                 Move (spareSpace, 0);
  671.             END;
  672.  
  673.         (* Erase area at end(s) of text *)
  674.         spareRect := box;
  675.         IF just = teFlushLeft THEN
  676.             spareRect.left := spareRect.left + lineWidth
  677.         ELSE
  678.             BEGIN
  679.                 IF just = teCenter THEN
  680.                     BEGIN
  681.                         spareRect.left := spareRect.left + spareSpace + lineWidth;
  682.                         EraseRect (spareRect)
  683.                     END;
  684.                 spareRect.left := box.left;
  685.                 spareRect.right := spareRect.left + spareSpace;
  686.             END;
  687.         IF NOT EmptyRect (spareRect) THEN
  688.             EraseRect (spareRect);
  689.  
  690.         (* Draw the line of text *)
  691.         DrawText (textLine, 0, length);
  692.  
  693.         (* Restore the port to its normal state *)
  694.         TextMode (currTextMode);
  695.         SetClip (currClip);
  696.         DisposeRgn (currClip)
  697.     END;
  698.  
  699.  
  700. {$S ProcessGuts}
  701. (*******************************************************************************
  702. * Private: FindProcessInfoWindow - Find a process info window for a process
  703. *
  704. * This routine searches the window list for a process info window that
  705. * represents the process with the process serial number specified by
  706. * "searchPSN".  Every process info window has a handle to the process serial
  707. * number of the process it represents in the refCon field of the window.  The
  708. * Process Manager routine, SameProcess, does the work of comparing the given
  709. * process serial number against the process serial number in the refCon.
  710. *
  711. * If a window for the specified process is found, a pointer to that window is
  712. * returned.  If there isn’t any window representing the given process, then NIL
  713. * is returned.
  714. *******************************************************************************)
  715.  
  716.     FUNCTION FindProcessInfoWindow (searchPSN: ProcessSerialNumber): WindowPtr;
  717.  
  718.         VAR
  719.             testWindow: WindowPtr; {Pointer to window we’re testing}
  720.             found:      Boolean;   {TRUE if matching process info window was found}
  721.             psnHandle:  Handle;    {Handle to PSN of window’s process info window}
  722.             error:      OSErr;
  723.  
  724.     BEGIN
  725.         found := FALSE;
  726.         testWindow := FrontWindow;
  727.  
  728.         (* Loop until the window is found or every window has been searched *)
  729.         WHILE (testWindow <> NIL) AND (NOT found) DO
  730.             BEGIN
  731.                 IF IsProcessInfoWindow (testWindow) THEN
  732.                     BEGIN
  733.                         (* Get the PSN of the window from its refCon *)
  734.                         psnHandle := Handle(GetWRefCon (testWindow));
  735.  
  736.                         (* Compare window’s PSN against searchPSN *)
  737.                         HLock (psnHandle);
  738.                         error := SameProcess (searchPSN, PSNPtr(psnHandle^)^,
  739.                                 (*<*)found);
  740.                         HUnlock (psnHandle)
  741.                     END;
  742.  
  743.                 (* Go to the next window in the window list *)
  744.                 IF NOT found THEN
  745.                     testWindow := WindowPtr(WindowPeek(testWindow)^.nextWindow)
  746.             END;
  747.  
  748.         (* Return pointer to matching process info window, or NIL if no match *)
  749.         FindProcessInfoWindow := testWindow
  750.     END;
  751.  
  752.  
  753. {$S ProcessGuts}
  754. (*******************************************************************************
  755. * Private: CreateProcessInfoWindow - Create a process info window
  756. *
  757. * This routine is called to create a new process info window and to display it
  758. * on the screen.  A pointer to the window is returned.  If there wasn’t enough
  759. * memory to open the new window, or if there was some other problem preventing
  760. * the window from being completely created, then an alert indicating the problem
  761. * is presented to the user and NIL is returned.
  762. *
  763. * I store the constant kProcessInfoWindKind into the windowKind field of the new
  764. * window.  When the routine IsProcessInfoWindow is called, it uses this field to
  765. * identify a window as a process info window.
  766. *******************************************************************************)
  767.  
  768.     FUNCTION CreateProcessInfoWindow: WindowPtr;
  769.  
  770.         VAR
  771.             aWindow: WindowPtr; {Pointer to the new window}
  772.             error:   OSErr;
  773.  
  774.         PROCEDURE HandleError (messageClass: Integer;
  775.                                messageIndex: Integer);
  776.  
  777.             VAR
  778.                 result: Integer; {Result of alert; ignored}
  779.  
  780.         BEGIN
  781.             IF aWindow <> NIL THEN
  782.                 CloseProcessInfoWindow (aWindow);
  783.             result := ShowStopAlert (messageClass, messageIndex);
  784.             gError := noErr;
  785.             CreateProcessInfoWindow := NIL;
  786.             EXIT (CreateProcessInfoWindow)
  787.         END;
  788.  
  789.     BEGIN
  790.         aWindow := NIL;
  791.  
  792.         (* Create the new window *)
  793.         aWindow := CreateDialog (rProcessInfoWindID);
  794.         IF aWindow = NIL THEN
  795.             IF gError = memFullErr THEN
  796.                 HandleError (rMemErrMessages, kMemErrProcInfoOpenMsg)
  797.             ELSE IF gError = resNotFound THEN
  798.                 HandleError (rResErrMessages, kResErrAppDamageMsg)
  799.             ELSE IF gError = dsSysErr THEN
  800.                 HandleError (rMiscErrMessages, kMiscErrUnknownMsg);
  801.  
  802.         (* Set up the window *)
  803.         SetPort (aWindow);
  804.         WindowPeek(aWindow)^.windowKind := kProcessInfoWindKind;
  805.  
  806.         (* Install the dialog items *)
  807.         error := InstallDialogItems (aWindow, rProcessInfoDitlID);
  808.         IF error <> noErr THEN
  809.             IF error = memFullErr THEN
  810.                 HandleError (rMemErrMessages, kMemErrProcInfoOpenMsg)
  811.             ELSE IF error = resNotFound THEN
  812.                 HandleError (rResErrMessages, kResErrAppDamageMsg)
  813.             ELSE IF error = dsSysErr THEN
  814.                 HandleError (rMiscErrMessages, kMiscErrUnknownMsg);
  815.  
  816.         CreateProcessInfoWindow := aWindow
  817.     END;
  818.  
  819.  
  820. {$S ProcessGuts}
  821. (*******************************************************************************
  822. * Private: DrawGrayLine - Draw a gray line into a dialog item
  823. *
  824. * DrawGrayLine draws a line from the top-left corner of "grayLineRect" to its
  825. * bottom-right corner.  On a non-Color QuickDraw Macintosh, this line is simply
  826. * drawn using the 50% gray pattern.  On a Color QuickDraw Macintosh, a gray
  827. * type-2 pattern is created with a gray color.  When this pattern is used to
  828. * draw to the screen, it is drawn using the specified color if possible.  If
  829. * there aren’t enough available colors, the color is dithered using the closest
  830. * available colors.
  831. *******************************************************************************)
  832.  
  833.     PROCEDURE DrawGrayLine (grayLineRect: Rect);
  834.  
  835.         VAR
  836.             qdVersion:   LongInt;      {QuickDraw version number}
  837.             grayColor:   RGBColor;     {Color of gray line}
  838.             grayPattern: PixPatHandle; {Handle to the gray pattern}
  839.             result:      OSErr;
  840.  
  841.     BEGIN
  842.         grayPattern := NIL;
  843.         PenNormal;
  844.  
  845.         (* See if Color QuickDraw is on this machine or not *)
  846.         result := Gestalt (gestaltQuickdrawVersion, (*<*)qdVersion);
  847.         IF qdVersion = gestaltOriginalQD THEN
  848.             (* Nope, just draw a 50% gray pattern *)
  849.             PenPat (gray)
  850.         ELSE
  851.             (* Yup, make a true gray pattern that can be dithered to the screen *)
  852.             BEGIN
  853.                 grayColor.red := $7FFF;
  854.                 grayColor.green := $7FFF;
  855.                 grayColor.blue := $7FFF;
  856.                 grayPattern := NewPixPat;
  857.                 MakeRGBPat (grayPattern, grayColor);
  858.                 PenPixPat (grayPattern);
  859.             END;
  860.  
  861.         (* Draw the line *)
  862.         MoveTo (grayLineRect.left, grayLineRect.top);
  863.         LineTo (grayLineRect.right, grayLineRect.bottom);
  864.  
  865.         (* Clean up *)
  866.         IF grayPattern <> NIL THEN
  867.             DisposPixPat (grayPattern);
  868.         PenNormal
  869.     END;
  870.  
  871.  
  872. {$S ProcessGuts}
  873. (*******************************************************************************
  874. * Private: SetUpProcessInfoItems - Set up process information static text items
  875. *
  876. * This routine sets up the text of the static text items in the process info
  877. * window specified by "processInfoWindow" to reflect the process information
  878. * passed in the "processInfo" parameter.  Only the process information that
  879. * doesn’t change while a process is active is set in this routine.  Information
  880. * that changes while a process is active is set and drawn in the
  881. * IdleProcessInfoWindow routine.
  882. *
  883. * Numbers are converted to strings using the FormatX2Str routine.  FormatX2Str
  884. * requires a script-independent canonical number format so that the resulting
  885. * string appears with the proper thousands separator regardless of the script
  886. * in use.  I previously created a canonical number format that has the form
  887. * ###,###,### in the U.S and saved it in a resource of type NUMF.
  888. *******************************************************************************)
  889.  
  890.     PROCEDURE SetUpProcessInfoItems (processInfoWindow: WindowPtr;
  891.                                      processInfo:       ProcessInfoRec);
  892.  
  893.         VAR
  894.             itemString:    Str255;       {"Application" or "Desk Accessory" string}
  895.             blankString:   Integer;      {Dummy empty string}
  896.             checkString:   StringPtr;    {Ptr either to check mark or blankString}
  897.             checkStrHnd:   StringHandle; {Handle to check mark string}
  898.             partitionSize: extended;     {Size of processes partition}
  899.             partsTable:    NumberParts;  {Number parts table from itl4 resource}
  900.             canonRsrc:     Handle;       {Hnd to canonical # format '###,###,###'}
  901.             status:        FormatStatus; {Status of #->String conversion}
  902.             success:       Boolean;      {TRUE if GetNumberParts call worked}
  903.  
  904.     BEGIN
  905.         (* Set process name *)
  906.         SetStatTextItem (processInfoWindow, kProcessNameItem, @processInfo.
  907.                 processName^ [1], ORD (processInfo.processName^ [0]));
  908.  
  909.         (* Set Application or Desk Accessory string *)
  910.         IF BAND (processInfo.processMode, modeDeskAccessory) <> 0 THEN
  911.             GetIndString ((*◊*)itemString, rAppOrDAStringID, kDAStringInd)
  912.         ELSE
  913.             GetIndString ((*◊*)itemString, rAppOrDAStringID, kAppStringInd);
  914.         SetStatTextItem (processInfoWindow, kAppOrDAItem, @itemString [1],
  915.                 ORD (itemString [0]));
  916.  
  917.         (* Set partition size item *)
  918.         partitionSize := processInfo.processSize DIV 1024;
  919.         success := GetNumberParts (partsTable);
  920.         IF success THEN
  921.             BEGIN
  922.                 (* Get the canonical number format I created earlier *)
  923.                 canonRsrc := Get1Resource ('NUMF', 0);
  924.                 IF canonRsrc <> NIL THEN
  925.                     BEGIN
  926.                         (* Convert partition size from extended to formatted string *)
  927.                         HLock (canonRsrc);
  928.                         status := FormatX2Str (partitionSize,
  929.                                 NumFormatStringPtr(canonRsrc^)^, partsTable,
  930.                                 (*<*)itemString);
  931.                         HUnlock (canonRsrc);
  932.  
  933.                         (* Set Total Size item to formatted partition size string *)
  934.                         SetStatTextItem (processInfoWindow, kTotalSizeItem,
  935.                                 @itemString [1], ORD (itemString [0]))
  936.                     END
  937.             END;
  938.  
  939.         (* Set type and creator *)
  940.         SetStatTextItem (processInfoWindow, kTypeItem, @processInfo.processType,
  941.                 SIZEOF (LongInt));
  942.         SetStatTextItem (processInfoWindow, kCreatorItem, @processInfo.
  943.                 processSignature, SIZEOF (OSType));
  944.  
  945.         (* Initialize the checkmark and blank strings *)
  946.         checkStrHnd := GetString (rCheckMarkID);
  947.         IF checkStrHnd <> NIL THEN
  948.             BlockMove (Ptr(checkStrHnd^), @itemString, ORD (checkStrHnd^^ [0]) + 1)
  949.         ELSE
  950.             itemString [0] := CHR (0);
  951.         blankString := 0;
  952.  
  953.         (* Check the suspend/resume flag *)
  954.         IF BAND (processInfo.processMode, modeNeedSuspendResume) <> 0 THEN
  955.             checkString := @itemString
  956.         ELSE
  957.             checkString := @blankString;
  958.         SetStatTextItem (processInfoWindow, kSusResChkItem, @checkString^ [1],
  959.                 ORD (checkString^ [0]));
  960.  
  961.         (* Check the window activate flag *)
  962.         IF BAND (processInfo.processMode, modeDoesActivateOnFGSwitch) <> 0 THEN
  963.             checkString := @itemString
  964.         ELSE
  965.             checkString := @blankString;
  966.         SetStatTextItem (processInfoWindow, kWindActChkItem, @checkString^ [1],
  967.                 ORD (checkString^ [0]));
  968.  
  969.         (* Check the window activate flag *)
  970.         IF BAND (processInfo.processMode, modeGetFrontClicks) <> 0 THEN
  971.             checkString := @itemString
  972.         ELSE
  973.             checkString := @blankString;
  974.         SetStatTextItem (processInfoWindow, kGetClickChkItem, @checkString^ [1],
  975.                 ORD (checkString^ [0]));
  976.  
  977.         (* Check the window activate flag *)
  978.         IF BAND (processInfo.processMode, modeGetAppDiedMsg) <> 0 THEN
  979.             checkString := @itemString
  980.         ELSE
  981.             checkString := @blankString;
  982.         SetStatTextItem (processInfoWindow, kAppDiedChkItem, @checkString^ [1],
  983.                 ORD (checkString^ [0]));
  984.  
  985.         (* Check the window activate flag *)
  986.         IF BAND (processInfo.processMode, modeStationeryAware) <> 0 THEN
  987.             checkString := @itemString
  988.         ELSE
  989.             checkString := @blankString;
  990.         SetStatTextItem (processInfoWindow, kStationeryChkItem, @checkString^ [1],
  991.                 ORD (checkString^ [0]));
  992.  
  993.         (* Check the window activate flag *)
  994.         IF BAND (processInfo.processMode, modeCanBackground) <> 0 THEN
  995.             checkString := @itemString
  996.         ELSE
  997.             checkString := @blankString;
  998.         SetStatTextItem (processInfoWindow, kCanBackChkItem, @checkString^ [1],
  999.                 ORD (checkString^ [0]));
  1000.  
  1001.         (* Check the window activate flag *)
  1002.         IF BAND (processInfo.processMode, modeOnlyBackground) <> 0 THEN
  1003.             checkString := @itemString
  1004.         ELSE
  1005.             checkString := @blankString;
  1006.         SetStatTextItem (processInfoWindow, kOnlyBackChkItem, @checkString^ [1],
  1007.                 ORD (checkString^ [0]));
  1008.  
  1009.         (* Check the window activate flag *)
  1010.         IF BAND (processInfo.processMode, modeHighLevelEventAware) <> 0 THEN
  1011.             checkString := @itemString
  1012.         ELSE
  1013.             checkString := @blankString;
  1014.         SetStatTextItem (processInfoWindow, kHighLevelChkItem, @checkString^ [1],
  1015.                 ORD (checkString^ [0]));
  1016.  
  1017.         (* Check the window activate flag *)
  1018.         IF BAND (processInfo.processMode, modeLocalAndRemoteHLEvents) <> 0 THEN
  1019.             checkString := @itemString
  1020.         ELSE
  1021.             checkString := @blankString;
  1022.         SetStatTextItem (processInfoWindow, kRHighLevelChkItem, @checkString^ [1],
  1023.                 ORD (checkString^ [0]));
  1024.  
  1025.         (* Check the window activate flag *)
  1026.         IF BAND (processInfo.processMode, modeMultiLaunch) <> 0 THEN
  1027.             checkString := @itemString
  1028.         ELSE
  1029.             checkString := @blankString;
  1030.         SetStatTextItem (processInfoWindow, kMultiUserChkItem, @checkString^ [1],
  1031.                 ORD (checkString^ [0]));
  1032.  
  1033.         (* Check the window activate flag *)
  1034.         IF BAND (processInfo.processMode, mode32BitCompatible) <> 0 THEN
  1035.             checkString := @itemString
  1036.         ELSE
  1037.             checkString := @blankString;
  1038.         SetStatTextItem (processInfoWindow, k32BitCleanChkItem, @checkString^ [1],
  1039.                 ORD (checkString^ [0]));
  1040.  
  1041.     END;
  1042.  
  1043.  
  1044. {$S ProcessGuts}
  1045. (*******************************************************************************
  1046. * Public: IdleProcessInfoWindow
  1047. *
  1048. * The memory indicator and the free memory readout are updated with the current
  1049. * values.
  1050. *
  1051. * The free memory readout is a static text item in the DITL, but there’s no text
  1052. * for it.  Instead, I’m drawing into that item’s rectangle using TextLineBox.
  1053. * I set the item up as a static text item just so that I can specify the type
  1054. * characteristics of the free memory readout from the DITL resource rather than
  1055. * hard-coding them in this routine.
  1056. *
  1057. * Numbers are converted to strings using the FormatX2Str routine.  FormatX2Str
  1058. * requires a script-independent canonical number format so that the resulting
  1059. * string appears with the proper thousands separator regardless of the script
  1060. * in use.  I previously created a canonical number format that has the form
  1061. * ###,###,### in the U.S and saved it in a resource of type NUMF.
  1062. *******************************************************************************)
  1063.  
  1064.     PROCEDURE IdleProcessInfoWindow (processInfoWindow: WindowPtr);
  1065.  
  1066.         VAR
  1067.             processInfo:   ProcessInfoRec; {Process info for window’s process}
  1068.             psnHandle:     Handle;         {Handle to PSN of window’s process}
  1069.             freeSpace:     extended;       {Amount of free space in partition}
  1070.             canonRsrc:     Handle;         {canonical # format '###,###,###'}
  1071.             freeSpaceStr:  Str255;         {String representation of freeSpace}
  1072.             status:        FormatStatus;   {Status of #->string conversion}
  1073.             partsTable:    NumberParts;    {Number parts table from itl4 resource}
  1074.             itemType:      TypeInfoRec;    {Type information for free mem readout}
  1075.             itemRect:      Rect;           {Rectangle of dialog item}
  1076.             freeAngle:     Integer;        {Angle between free and full memory}
  1077.             aColor:        RGBColor;       {Color to draw memory indicator}
  1078.             qdVersion:     LongInt;        {Version of QuickDraw on this machine}
  1079.             success:       Boolean;        {TRUE if GetNumberParts call worked}
  1080.             error:         OSErr;
  1081.  
  1082.     BEGIN
  1083.         SetPort (processInfoWindow);
  1084.         PenNormal;
  1085.  
  1086.         (* Get the PSN of the process associated with processInfoWindow *)
  1087.         psnHandle := Handle(GetWRefCon (processInfoWindow));
  1088.  
  1089.         (* Get information about an open process *)
  1090.         processInfo.processInfoLength := SIZEOF (ProcessInfoRec);
  1091.         processInfo.processName := NIL;
  1092.         processInfo.processAppSpec := NIL;
  1093.         HLock (psnHandle);
  1094.         error := GetProcessInformation (PSNPtr(psnHandle^)^, (*◊*)processInfo);
  1095.         HUnlock (psnHandle);
  1096.  
  1097.         (* Check to see whether the process still exists *)
  1098.         IF error = procNotFound THEN
  1099.             (* Process terminated, so close this process info window *)
  1100.             CloseProcessInfoWindow (processInfoWindow)
  1101.         ELSE
  1102.             BEGIN
  1103.                 (* Starting here, convert amount of free space to a string *)
  1104.                 freeSpace := processInfo.processFreeMem DIV 1024;
  1105.  
  1106.                 (* Get number parts table from itl4 *)
  1107.                 success := GetNumberParts ((*<*)partsTable);
  1108.                 IF success THEN
  1109.                     BEGIN
  1110.                         (* Get my canonical number format *)
  1111.                         canonRsrc := Get1Resource ('NUMF', 0);
  1112.                         IF canonRsrc <> NIL THEN
  1113.                             BEGIN
  1114.                                 (* Convert free space to equivalent string *)
  1115.                                 HLock (canonRsrc);
  1116.                                 status := FormatX2Str (freeSpace,
  1117.                                         NumFormatStringPtr(canonRsrc^)^, partsTable,
  1118.                                         (*<*)freeSpaceStr);
  1119.                                 HUnlock (canonRsrc);
  1120.  
  1121.                                 (* Get the item rectangle of the free-space readout *)
  1122.                                 GetDialogItemRect (processInfoWindow, kFreeSpaceItem,
  1123.                                         (*<*)itemRect);
  1124.  
  1125.                                 (* Get the font characteristics of the stat text item *)
  1126.                                 GetStatTextFontInfo (processInfoWindow, kFreeSpaceItem,
  1127.                                         (*<*)itemType);
  1128.  
  1129.                                 (* Draw the free-space readout *)
  1130.                                 TextFont (itemType.typeFace);
  1131.                                 TextSize (itemType.typeSize);
  1132.                                 TextFace (itemType.typeStyle);
  1133.                                 TextLineBox (@freeSpaceStr [1], ORD (freeSpaceStr [0]),
  1134.                                         itemRect, itemType.textJust)
  1135.                             END;
  1136.                     END;
  1137.  
  1138.                 (* Draw the memory indicator frame *)
  1139.                 GetDialogItemRect (processInfoWindow, kMemIndicatorItem,
  1140.                         (*<*)itemRect);
  1141.                 FrameOval (itemRect);
  1142.                 InsetRect ((*◊*)itemRect, 1, 1);
  1143.  
  1144.                 (* Calc angle in the memory indicator that the free memory begins *)
  1145.                 freeAngle := processInfo.processFreeMem * 360 DIV processInfo.
  1146.                         processSize;
  1147.  
  1148.                 (* Draw the memory indicator *)
  1149.                 error := Gestalt (gestaltQuickdrawVersion, (*<*)qdVersion);
  1150.                 IF qdVersion = gestaltOriginalQD THEN
  1151.                     PenPat (black)
  1152.                 ELSE
  1153.                     BEGIN
  1154.                         PmForeColor (kUsedColor);
  1155.                         PmBackColor (0)
  1156.                     END;
  1157.  
  1158.                 (* Draw the used memory part of the memory indicator *)
  1159.                 PaintArc (itemRect, 0, 360 - freeAngle);
  1160.  
  1161.                 (* Set the color of the free memory part of the indicator *)
  1162.                 IF qdVersion = gestaltOriginalQD THEN
  1163.                     PenPat (white)
  1164.                 ELSE
  1165.                     BEGIN
  1166.                         PmForeColor (kFreeColor);
  1167.                         PmBackColor (1)
  1168.                     END;
  1169.  
  1170.                 (* Draw the free memory part of the memory indicator *)
  1171.                 PaintArc (itemRect, 360 - freeAngle, freeAngle);
  1172.  
  1173.                 (* Reset the port characteristics back to normal *)
  1174.                 PenNormal;
  1175.                 IF qdVersion <> gestaltOriginalQD THEN
  1176.                     BEGIN
  1177.                         PmForeColor (1);
  1178.                         PmBackColor (0)
  1179.                     END
  1180.             END
  1181.     END;
  1182.  
  1183.  
  1184. {$S ProcessGuts}
  1185. (*******************************************************************************
  1186. * Public: DrawProcessInfoWindow
  1187. *
  1188. * The Dialog Utility routine, DrawDialogItems, is called to draw all the
  1189. * standard dialog items in the processInfoWindow specified by processInfoWindow.
  1190. * Then, the cosmetic gray lines are drawn.  The memory readouts aren’t drawn
  1191. * because they’re drawn in IdleProcessInfoWindow.
  1192. *******************************************************************************)
  1193.  
  1194.     PROCEDURE DrawProcessInfoWindow (processInfoWindow: WindowPtr);
  1195.  
  1196.         VAR
  1197.             grayLineRect: Rect; {Rectangle of gray line item}
  1198.  
  1199.     BEGIN
  1200.         (* Draw the standard dialog items *)
  1201.         DrawDialogItems (processInfoWindow);
  1202.  
  1203.         (* Draw the two gray, cosmetic, separating lines *)
  1204.         GetDialogItemRect (processInfoWindow, kGrayLineItem0, (*<*)grayLineRect);
  1205.         DrawGrayLine (grayLineRect);
  1206.         GetDialogItemRect (processInfoWindow, kGrayLineItem1, (*<*)grayLineRect);
  1207.         DrawGrayLine (grayLineRect)
  1208.     END;
  1209.  
  1210.  
  1211. {$S ProcessGuts}
  1212. (*******************************************************************************
  1213. * Public: FixProcessInfoMenus
  1214. *
  1215. * If there’s enough memory to work with, the launch items in the File menu are
  1216. * enabled.
  1217. *******************************************************************************)
  1218.  
  1219.     PROCEDURE FixProcessInfoMenus (processInfoWindow: WindowPtr);
  1220.  
  1221.         VAR
  1222.             aMenu: MenuHandle; {Handle to any menu we’re checking on}
  1223.  
  1224.     BEGIN
  1225.         (* Undim the File menu items *)
  1226.         aMenu := GetMHandle (mFile);
  1227.         IF NOT FailLowMemory (0) THEN
  1228.             BEGIN
  1229.                 EnableItem (aMenu, iLaunchFore);
  1230.                 EnableItem (aMenu, iLaunchBack);
  1231.                 EnableItem (aMenu, iLaunchTo)
  1232.             END
  1233.     END;
  1234.  
  1235.  
  1236. {$S ProcessGuts}
  1237. (*******************************************************************************
  1238. * Public: CloseProcessInfoWindow
  1239. *
  1240. * This should be pretty easy to figure out.
  1241. *******************************************************************************)
  1242.  
  1243.     PROCEDURE CloseProcessInfoWindow (processInfoWindow: WindowPtr);
  1244.  
  1245.         VAR
  1246.             psnHandle: Handle; {Handle to the PSN of process the window represents}
  1247.  
  1248.     BEGIN
  1249.         DisposHandle (Handle(GetWRefCon (processInfoWindow)));
  1250.         CloseWindow (processInfoWindow);
  1251.         DisposeDialogItems (processInfoWindow);
  1252.         DisposPtr (Ptr(processInfoWindow))
  1253.     END;
  1254.  
  1255.  
  1256. {$S ProcessGuts}
  1257. (*******************************************************************************
  1258. * Public: IdleAllProcessWindows
  1259. *
  1260. * The process list window and process info windows each have their own idle
  1261. * routine defined in this source file, so the type of window is checked and the
  1262. * appropriate idle routine is called for that window.
  1263. *******************************************************************************)
  1264.  
  1265.     PROCEDURE IdleAllProcessWindows;
  1266.  
  1267.         VAR
  1268.             processWindow: WindowPtr; {Pointer to each process window being idled}
  1269.  
  1270.     BEGIN
  1271.         processWindow := FrontWindow;
  1272.  
  1273.         (* Loop through all windows in the window list *)
  1274.         WHILE processWindow <> NIL DO
  1275.             BEGIN
  1276.                 (* Call the appropriate idle routine if it’s a process window *)
  1277.                 IF IsProcessListWindow (processWindow) THEN
  1278.                     IdleProcessListWindow (processWindow)
  1279.                 ELSE IF IsProcessInfoWindow (processWindow) THEN
  1280.                     IdleProcessInfoWindow (processWindow);
  1281.  
  1282.                 (* Go to the next window in the window list *)
  1283.                 processWindow := WindowPtr(WindowPeek(processWindow)^.nextWindow)
  1284.             END
  1285.     END;
  1286.  
  1287.  
  1288. {$S ProcessGuts}
  1289. (*******************************************************************************
  1290. * Private: AppDAFilter - File filter procedure for apps and files with DAs
  1291. *
  1292. *     This is a Standard File file filter procedure that allows applications and
  1293. * any files with desk accessories in them to show up in the Standard File file
  1294. * list.
  1295. *
  1296. *     Checking to see whether a file is an application is easy enough.  Just
  1297. * check to see whether its type is APPL.  If it is, then it’s an application.
  1298. * Checking on desk accessories is trickier.  Desk accessories can be contained
  1299. * in any type of file.  So if a file isn’t doesn’t have the APPL type, I open
  1300. * the resource fork of the file using HOpenResFile and an access mode of
  1301. * fdRdPerm.  This allows me to open and close the resource file without worrying
  1302. * about that resource file being open by someone else because HOpenResFile with
  1303. * an access mode of fdRdPerm returns a unique access path to this routine.  When
  1304. * the file is open, I check for DRVR resources.  DRVR resources can be either
  1305. * desk accessories or device drivers.  I only want to show files containing desk
  1306. * accessories, so I check on the first character of the DRVR resource’s name.
  1307. * If it’s a null character, then the DRVR is a desk accessory.  If it’s any
  1308. * other character, then it’s a device driver and I ignore it.
  1309. *******************************************************************************)
  1310.  
  1311.     FUNCTION AppDAFilter (fileInfo: CInfoPBPtr): Boolean;
  1312.  
  1313.         CONST
  1314.             kShowIt = FALSE; {FALSE means I do not filter out...}
  1315.  
  1316.         TYPE
  1317.             LongIntPtr = ^LongInt;
  1318.  
  1319.         VAR
  1320.             resRef:     Integer; {File ref num of file being tested}
  1321.             currResRef: Integer; {File ref number of current file}
  1322.             numDrvrs:   Integer; {Number of DRVR resources in file being tested}
  1323.             index:      Integer; {Index into resources of file being tested}
  1324.             drvrRsrc:   Handle;  {Handle to DRVR resource; always NIL master ptr}
  1325.             resID:      Integer; {Resource ID of DRVR resource; ignored}
  1326.             resType:    ResType; {Resource type of DRVR resource; ignored}
  1327.             resName:    Str255;  {Resource name of DRVR resource}
  1328.  
  1329.     BEGIN
  1330.         IF fileInfo^.ioFlFndrInfo.fdType ='APPL' THEN
  1331.             AppDAFilter := kShowIt
  1332.         ELSE
  1333.             BEGIN
  1334.                 (* Assume we don’t show the file *)
  1335.                 AppDAFilter := NOT kShowIt;
  1336.  
  1337.                 (* Want to check rsrcs, not load ’em, including preload resources *)
  1338.                 SetResLoad (FALSE);
  1339.  
  1340.                 (* Save current res file refnum, open the specified rsrc file *)
  1341.                 currResRef := CurResFile;
  1342.                 resRef := HOpenResFile (fileInfo^.ioVRefNum,
  1343.                         LongIntPtr(CurDirStore)^, fileInfo^.ioNamePtr^, fsRdPerm);
  1344.  
  1345.                 (* If couldn’t open resource file, HOpenResFile returns -1 *)
  1346.                 IF (resRef <> -1) THEN
  1347.                     BEGIN
  1348.                         UseResFile (resRef);
  1349.  
  1350.                         (* Count number of DRVR resources in the file *)
  1351.                         numDrvrs := Count1Resources ('DRVR');
  1352.                         IF numDrvrs > 0 THEN
  1353.                             BEGIN
  1354.                                 (* For each DRVR, see if it’s a DA *)
  1355.                                 FOR index := 1 TO numDrvrs DO
  1356.                                     BEGIN
  1357.                                         drvrRsrc := Get1IndResource ('DRVR', index);
  1358.                                         GetResInfo (drvrRsrc, (*<*)resID, (*<*)resType,
  1359.                                                 (*<*)resName);
  1360.         
  1361.                                         (* If first char of name is null, it’s a DA *)
  1362.                                         IF resName [1] = CHR (0) THEN
  1363.                                             AppDAFilter := kShowIt
  1364.                                     END
  1365.                             END;
  1366.                         CloseResFile (resRef)
  1367.                     END;
  1368.         
  1369.                 (* Restore everything back to what it was *)
  1370.                 UseResFile (currResRef);
  1371.                 SetResLoad (TRUE)
  1372.             END
  1373.     END;
  1374.  
  1375.  
  1376. {$S ProcessGuts}
  1377. (*******************************************************************************
  1378. * Private: LaunchCycle - Attempt to launch a process
  1379. *
  1380. * This routine calls the LaunchProcess routine that’s in the UProcessUtils unit.
  1381. * The launchFile parameter specifies the file to launch.  The docList parameter
  1382. * specifies the list of documents to pass to the launched application for it to
  1383. * open or print.  The launchOptions parameter specifies the initial set of
  1384. * launch options to use when launching.  The the section titled “Specifying
  1385. * Launch Options” in the Process Manager chapter of Inside Macintosh VI for the
  1386. * a list and description of the launch options that you can pass in this
  1387. * parameter.
  1388. *
  1389. * If the Process Manager denies the launch, then LaunchProcess returns the
  1390. * resulting error code in the LaunchError flag.  If this happens and if the
  1391. * error happened to be that the machine is in 32-bit addressing mode and the
  1392. * application’s SIZE resource doesn’t have the 32-bit clean flag on, or if there
  1393. * isn’t enough memory to launch the application or desk accessory, then an alert
  1394. * is presented to the user asking if he or she wants to continue anyway.  If the
  1395. * user specifies that he or she does, then launch options are added to the ones
  1396. * passed in the launchOptions parameter which allow 32-bit unclean applications
  1397. * to launch or to allow the launch into available memory, and then LaunchProcess
  1398. * is called again.  This is repeated either until the application or desk
  1399. * accessory is successfully launched, the user chose not to launch it, or until
  1400. * an unrecoverable error occurs.
  1401. *******************************************************************************)
  1402.  
  1403.     PROCEDURE LaunchCycle (launchFile:    FSSpec;
  1404.                            docList:       DocListHnd;
  1405.                            launchOptions: Integer);
  1406.  
  1407.         VAR
  1408.             processNum:    ProcessSerialNumber; {Serial number of launched process}
  1409.             attemptLaunch: Boolean;             {TRUE if continuing launch attempt}
  1410.             result:        Integer;             {Result of caution alert}
  1411.             launchError:   OSErr;               {Launch error code}
  1412.             error:         OSErr;
  1413.  
  1414.     BEGIN
  1415.         (* Repeat until successful launch or cancelled launch *)
  1416.         REPEAT
  1417.             (* Attempt to launch the process *)
  1418.             error := LaunchProcess (launchFile, NIL, docList, launchOptions,
  1419.                     (*<*)processNum, (*<*)launchError);
  1420.  
  1421.             (* Check for launching errors *)
  1422.             IF launchError <> noErr THEN
  1423.                 BEGIN
  1424.                     (* There was a launching error, present to user *)
  1425.                     IF launchError = appModeErr THEN
  1426.                         BEGIN
  1427.                             (* Ask user if it’s OK to launch 32-bit unclean app *)
  1428.                             result := ShowCautionOKCancelAlert (rMiscWrnMessages,
  1429.                                     kMiscWrnUncleanMsg);
  1430.                             IF result = ok THEN
  1431.                                 BEGIN
  1432.                                     (* Try launch again, allowing 32-bit unclean app *)
  1433.                                     launchOptions := BOR (launchOptions,
  1434.                                             launchAllow24Bit);
  1435.                                     attemptLaunch := TRUE
  1436.                                 END
  1437.                             ELSE
  1438.                                 attemptLaunch := FALSE
  1439.                         END
  1440.                     ELSE IF launchError = memFullErr THEN
  1441.                         BEGIN
  1442.                             (* Ask user if it’s OK to launch w/ < requested memory *)
  1443.                             result := ShowCautionOKCancelAlert (rMiscWrnMessages,
  1444.                                     kMiscWrnLaunchMemMsg);
  1445.                             IF result = ok THEN
  1446.                                 BEGIN
  1447.                                     (* Try launch again, with less than requested mem *)
  1448.                                     launchOptions := BOR (launchOptions,
  1449.                                             launchUseMinimum);
  1450.                                     attemptLaunch := TRUE
  1451.                                 END
  1452.                             ELSE
  1453.                                 attemptLaunch := FALSE
  1454.                         END
  1455.                     ELSE
  1456.                         BEGIN
  1457.                             (* Some error we don’t handle happened *)
  1458.                             result := ShowStopAlert (rMiscErrMessages,
  1459.                                     kMiscErrUnknownMsg);
  1460.                             attemptLaunch := FALSE
  1461.                         END
  1462.                 END
  1463.             ELSE IF error <> noErr THEN
  1464.                 BEGIN
  1465.                     result := ShowStopAlert (rMiscErrMessages,
  1466.                             kMiscErrUnknownMsg);
  1467.                     attemptLaunch := FALSE
  1468.                 END
  1469.             ELSE
  1470.                 attemptLaunch := FALSE
  1471.         UNTIL NOT attemptLaunch;
  1472.     END;
  1473.  
  1474.  
  1475. {$S ProcessGuts}
  1476. (*******************************************************************************
  1477. * Public: DoLaunchInFront
  1478. *
  1479. * If the user is launching with documents, then only applications are presented
  1480. * to the user in the standard-file dialog.  If the user only wants to launch
  1481. * without any documents, then both applications and files containing desk
  1482. * accessories are presented to the user.
  1483. *******************************************************************************)
  1484.  
  1485.     PROCEDURE DoLaunchInFront;
  1486.  
  1487.         VAR
  1488.             reply:       StandardFileReply; {Reply from SFGetFile}
  1489.             typeList:    SFTypeList;        {List of file types for SF}
  1490.             launchSpec:  FSSpec;            {Location of selected app/DA}
  1491.             docList:     DocListHnd;        {Handle to the document list}
  1492.             gettingDocs: Boolean;           {True if user still getting docs}
  1493.             launchMode:  LaunchModeCode;    {Current launch mode}
  1494.             error:       OSErr;
  1495.  
  1496.     BEGIN
  1497.         (* Get the user’s choice for a file to launch *)
  1498.         launchMode := GetLaunchMode;
  1499.         IF launchMode = kJustLaunch THEN
  1500.             (* Just launching, so launch applications and DAs *)
  1501.             StandardGetFile (@AppDAFilter, -1, typeList, (*<*)reply)
  1502.         ELSE IF (launchMode = kOpenLaunch) OR (launchMode = kPrintLaunch) THEN
  1503.             BEGIN
  1504.                 (* Launching with documents, so launch applications only *)
  1505.                 typeList [0] := 'APPL';
  1506.                 StandardGetFile (NIL, 1, typeList, (*<*)reply)
  1507.             END;
  1508.  
  1509.         IF reply.sfGood THEN
  1510.             BEGIN
  1511.                 launchSpec := reply.sfFile;
  1512.  
  1513.                 (* Check to see if documents should be opened/printed as well *)
  1514.                 IF (launchMode = kOpenLaunch) OR (launchMode = kPrintLaunch) THEN
  1515.                     BEGIN
  1516.                         (* Create an empty list of documents *)
  1517.                         docList := CreateDocList (launchMode);
  1518.  
  1519.                         (* Keep getting documents until user chooses Cancel *)
  1520.                         gettingDocs := TRUE;
  1521.                         WHILE gettingDocs DO
  1522.                             BEGIN
  1523.                                 StandardGetFile (NIL, -1, typeList, (*<*)reply);
  1524.                                 IF reply.sfGood THEN
  1525.                                     error := AddToDocList (reply.sfFile, (*◊*)docList)
  1526.                                 ELSE
  1527.                                     gettingDocs := FALSE
  1528.                             END
  1529.                     END
  1530.                 ELSE
  1531.                     docList := NIL;
  1532.  
  1533.                 (* Attempt to launch the application *)
  1534.                 LaunchCycle (launchSpec, docList, launchContinue);
  1535.  
  1536.                 (* Dispose of the document list, if there was one *)
  1537.                 IF docList <> NIL THEN
  1538.                     DisposeDocList (docList)
  1539.             END
  1540.     END;
  1541.  
  1542.  
  1543. {$S ProcessGuts}
  1544. (*******************************************************************************
  1545. * Public: DoLaunchInBack
  1546. *
  1547. * For the moment, I’m using SFGetFile to choose files rather than
  1548. * StandardGetFile because StandardGetFile has a bug in that file filtering
  1549. * doesn’t work right.  This is supposed to be fixed in b2, so I’ll change this
  1550. * call once that version is released.  This will make the call to FSMakeFSSpec
  1551. * unnecessary because StandardGetFile returns the FSSpec of the chosen file.
  1552. *******************************************************************************)
  1553.  
  1554.     PROCEDURE DoLaunchInBack;
  1555.  
  1556.         VAR
  1557.             reply:       StandardFileReply; {Reply from SFGetFile}
  1558.             typeList:    SFTypeList;        {List of file types to diplay in SF}
  1559.             launchSpec:  FSSpec;            {Location of selected application}
  1560.             docList:     DocListHnd;        {Handle to the document list}
  1561.             gettingDocs: Boolean;           {True if user still getting docs}
  1562.             launchMode:  LaunchModeCode;    {Current launch mode}
  1563.             error:       OSErr;
  1564.  
  1565.     BEGIN
  1566.         (* Get the user’s choice for an application to launch *)
  1567.         typeList [0] := 'APPL';
  1568.         StandardGetFile (NIL, 1, typeList, (*<*)reply);
  1569.  
  1570.         IF reply.sfGood THEN
  1571.             BEGIN
  1572.                 (* Convert working directory and file name to FSSpec *)
  1573.                 launchSpec := reply.sfFile;
  1574.  
  1575.                 launchMode := GetLaunchMode;
  1576.                 IF (launchMode = kOpenLaunch) OR (launchMode = kPrintLaunch) THEN
  1577.                     BEGIN
  1578.                         (* Create an empty list of documents *)
  1579.                         docList := CreateDocList (launchMode);
  1580.  
  1581.                         (* Keep getting documents until user chooses Cancel *)
  1582.                         gettingDocs := TRUE;
  1583.                         WHILE gettingDocs DO
  1584.                             BEGIN
  1585.                                 StandardGetFile (NIL, -1, typeList, (*<*)reply);
  1586.                                 IF reply.sfGood THEN
  1587.                                     error := AddToDocList (reply.sfFile, (*◊*)docList)
  1588.                                 ELSE
  1589.                                     gettingDocs := FALSE
  1590.                             END
  1591.                     END
  1592.                 ELSE
  1593.                     docList := NIL;
  1594.  
  1595.                 (* Attempt to launch the application *)
  1596.                 LaunchCycle (launchSpec, docList, launchContinue +
  1597.                         launchDontSwitch);
  1598.  
  1599.                 (* Dispose of the document list, if there was one *)
  1600.                 IF docList <> NIL THEN
  1601.                     DisposeDocList (docList)
  1602.             END
  1603.     END;
  1604.  
  1605.  
  1606. {$S ProcessGuts}
  1607. (*******************************************************************************
  1608. * Public: DoLaunchTo
  1609. *
  1610. * For the moment, I’m using SFGetFile to choose files rather than
  1611. * StandardGetFile because StandardGetFile has a bug in that file filtering
  1612. * doesn’t work right.  This is supposed to be fixed in b2, so I’ll change this
  1613. * call once that version is released.  This will make the call to FSMakeFSSpec
  1614. * unnecessary because StandardGetFile returns the FSSpec of the chosen file.
  1615. *******************************************************************************)
  1616.  
  1617.     PROCEDURE DoLaunchTo;
  1618.  
  1619.         VAR
  1620.             reply:       StandardFileReply; {Reply from SFGetFile}
  1621.             typeList:    SFTypeList;        {List of file types to diplay in SF}
  1622.             launchSpec:  FSSpec;            {Location of selected file}
  1623.             docList:     DocListHnd;        {Handle to the document list}
  1624.             gettingDocs: Boolean;           {True if user still getting docs}
  1625.             launchMode:  LaunchModeCode;    {Current launch mode}
  1626.             error:       OSErr;
  1627.  
  1628.     BEGIN
  1629.         (* Get the user’s choice for a file to launch *)
  1630.         launchMode := GetLaunchMode;
  1631.         IF launchMode = kJustLaunch THEN
  1632.             (* Just launching, so launch applications and DAs *)
  1633.             StandardGetFile (@AppDAFilter, -1, typeList, (*<*)reply)
  1634.         ELSE IF (launchMode = kOpenLaunch) OR (launchMode = kPrintLaunch) THEN
  1635.             BEGIN
  1636.                 (* Launching with documents, so launch applications only *)
  1637.                 typeList [0] := 'APPL';
  1638.                 StandardGetFile (NIL, 1, typeList, (*<*)reply)
  1639.             END;
  1640.  
  1641.         IF reply.sfGood THEN
  1642.             BEGIN
  1643.                 (* Convert working directory and file name to FSSpec *)
  1644.                 launchSpec := reply.sfFile;
  1645.  
  1646.                 IF (launchMode = kOpenLaunch) OR (launchMode = kPrintLaunch) THEN
  1647.                     BEGIN
  1648.                         (* Create an empty list of documents *)
  1649.                         docList := CreateDocList (launchMode);
  1650.  
  1651.                         (* Keep getting documents until user chooses Cancel *)
  1652.                         gettingDocs := TRUE;
  1653.                         WHILE gettingDocs DO
  1654.                             BEGIN
  1655.                                 StandardGetFile (NIL, -1, typeList, (*<*)reply);
  1656.                                 IF reply.sfGood THEN
  1657.                                     error := AddToDocList (reply.sfFile, (*◊*)docList)
  1658.                                 ELSE
  1659.                                     gettingDocs := FALSE
  1660.                             END
  1661.                     END
  1662.                 ELSE
  1663.                     docList := NIL;
  1664.  
  1665.                 (* Attempt to launch the application or DA *)
  1666.                 LaunchCycle (launchSpec, docList, launchAllow24Bit);
  1667.  
  1668.                 (* Dispose of the document list, if there was one *)
  1669.                 IF docList <> NIL THEN
  1670.                     DisposeDocList (docList)
  1671.             END
  1672.     END;
  1673.  
  1674.  
  1675. {$S ProcessGuts}
  1676. (*******************************************************************************
  1677. * Public: DoLaunchMode
  1678. *
  1679. * SetLaunchMode does most of the work, and there isn’t much to do.
  1680. *******************************************************************************)
  1681.  
  1682.     PROCEDURE DoLaunchMode (modeItem: Integer);
  1683.  
  1684.     BEGIN
  1685.         CASE modeItem OF
  1686.             iJustLaunch:
  1687.                 SetLaunchMode (kJustLaunch);
  1688.             iOpenLaunch:
  1689.                 SetLaunchMode (kOpenLaunch);
  1690.             iPrintLaunch:
  1691.                 SetLaunchMode (kPrintLaunch)
  1692.         END
  1693.     END;
  1694.  
  1695.  
  1696. {$S ProcessGuts}
  1697. (*******************************************************************************
  1698. * Public: DoBringProcessToFront
  1699. *
  1700. * The List Manager is called to get each selection in the process list window.
  1701. * SetFrontProcess is called with the process serial number of each selected
  1702. * process.  They aren’t immediately brought to the front when SetFrontProcess is
  1703. * called.  Instead, they are scheduled to come to the front in the same order as
  1704. * they were presented to SetFrontProcess.  Once ProcDoggie reenters the main
  1705. * event loop, the Process Manager brings each scheduled process to the front in
  1706. * turn.
  1707. *
  1708. * At the moment, I can’t get ProcDoggie itself to be scheduled.  I assume it’s
  1709. * because SetFrontProcess checks to see if process serial number you passed it
  1710. * is the same as the process serial number of the current process.  If it is, it
  1711. * doesn’t bother to schedule the process.  I’m not quite sure how to work around
  1712. * that.
  1713. *******************************************************************************)
  1714.  
  1715.     PROCEDURE DoBringProcessToFront (processListWindow: WindowPtr);
  1716.  
  1717.         CONST
  1718.             kFindNext = TRUE; {Pass to LGetSelect to find sequence of selections}
  1719.  
  1720.         VAR
  1721.             procList:     ListHandle;         {Handle to List Mgr process list}
  1722.             currCell:     Point;              {Cell that has selection}
  1723.             listInfo:     ProcessListInfoRec; {Process info from List Mgr list}
  1724.             gotSelection: Boolean;            {T if got sel’d cell, F if no more}
  1725.             listInfoLen:  Integer;            {Length of list info in bytes}
  1726.             error:        OSErr;
  1727.  
  1728.     BEGIN
  1729.         (* Get the List Manager’s copy of the process list *)
  1730.         procList := ListHandle(GetWRefCon (processListWindow));
  1731.  
  1732.         (* Keep looping until all selected processes have been brought to front *)
  1733.         currCell.v := 0;
  1734.         currCell.h := 0;
  1735.         gotSelection := TRUE;
  1736.         WHILE gotSelection DO
  1737.             BEGIN
  1738.                 gotSelection := LGetSelect (kFindNext, (*◊*)currCell, procList);
  1739.                 IF gotSelection THEN
  1740.                     BEGIN
  1741.                         listInfoLen := SIZEOF (ProcessListInfoRec);
  1742.                         LGetCell (Ptr(@listInfo), (*◊*)listInfoLen, currCell,
  1743.                                 procList);
  1744.                         error := SetFrontProcess (listInfo.serialNumber);
  1745.                         currCell.v := currCell.v + 1
  1746.                     END
  1747.             END
  1748.     END;
  1749.  
  1750.  
  1751. {$S ProcessGuts}
  1752. (*******************************************************************************
  1753. * Public: DoGetProcessInfo
  1754. *
  1755. * This routine loops until Process Information windows for all selected
  1756. * processes in the Process List window are displayed.  Information for each
  1757. * process in the process list is retrieved from the list itself.  Then, that
  1758. * process is compared against all existing Process Information windows.  If a
  1759. * Process Information window already exists for that process, then that window
  1760. * is simply activated and DoGetProcessInfo exits.  Otherwise, the Process
  1761. * Manager is called to retrieve information for that process.  A new Process
  1762. * Information window is created, and its contents are set to the information
  1763. * retrieved for the process.
  1764. *******************************************************************************)
  1765.  
  1766.     PROCEDURE DoGetProcessInfo (processListWindow: WindowPtr);
  1767.  
  1768.         CONST
  1769.             kFindNext = TRUE; {Pass to LGetSelect to find sequence of selections}
  1770.  
  1771.         VAR
  1772.             procList:          ListHandle;         {Handle to List Mgr proc list}
  1773.             currCell:          Point;              {Cell that has selection}
  1774.             listInfo:          ProcessListInfoRec; {Proc info from List Mgr list}
  1775.             gotSelection:      Boolean;            {T if got sel’d cell, F if none}
  1776.             listInfoLen:       Integer;            {Length of list info in bytes}
  1777.             processInfo:       ProcessInfoRec;     {Info about selected processes}
  1778.             procName:          Str31;              {Name of selected processes}
  1779.             procSpec:          FSSpec;             {File spec of sel’d processes}
  1780.             processInfoWindow: WindowPtr;          {Ptr to new process info window}
  1781.             psnHandle:         Handle;             {Handle to PSN of chosen proc}
  1782.             existingWindow:    WindowPtr;          {Proc info wind if already open}
  1783.             error:             OSErr;
  1784.  
  1785.         PROCEDURE HandleError (messageClass: Integer;
  1786.                                messageIndex: Integer);
  1787.  
  1788.             VAR
  1789.                 result: Integer; {Result of alert; ignored}
  1790.  
  1791.         BEGIN
  1792.             IF processInfoWindow <> NIL THEN
  1793.                 CloseProcessInfoWindow (processInfoWindow);
  1794.             result := ShowStopAlert (messageClass, messageIndex);
  1795.             gError := noErr;
  1796.             EXIT (DoGetProcessInfo)
  1797.         END;
  1798.  
  1799.     BEGIN
  1800.         (* Get the List Manager’s copy of the process list *)
  1801.         procList := ListHandle(GetWRefCon (processListWindow));
  1802.  
  1803.         (* Keep looping until all selected processes have been brought to front *)
  1804.         currCell.v := 0;
  1805.         currCell.h := 0;
  1806.         gotSelection := TRUE;
  1807.         WHILE gotSelection DO
  1808.             BEGIN
  1809.                 gotSelection := LGetSelect (kFindNext, (*◊*)currCell, procList);
  1810.                 IF gotSelection THEN
  1811.                     BEGIN
  1812.                         listInfoLen := SIZEOF (ProcessListInfoRec);
  1813.                         LGetCell (Ptr(@listInfo), (*◊*)listInfoLen, currCell,
  1814.                                 procList);
  1815.  
  1816.                         (* See if proc info wind already exists for selected proc *)
  1817.                         existingWindow := FindProcessInfoWindow (listInfo.
  1818.                                 serialNumber);
  1819.                         IF existingWindow <> NIL THEN
  1820.                             SelectWindow (existingWindow)
  1821.                         ELSE
  1822.                             BEGIN
  1823.                                 (* Get information about an open process *)
  1824.                                 processInfo.processInfoLength :=
  1825.                                         SIZEOF (ProcessInfoRec);
  1826.                                 processInfo.processName := @procName;
  1827.                                 processInfo.processAppSpec := @procSpec;
  1828.                                 error := GetProcessInformation (listInfo.serialNumber,
  1829.                                         (*◊*)processInfo);
  1830.                                 IF error <> noErr THEN
  1831.                                     HandleError (rMiscErrMessages, kMiscErrUnknownMsg);
  1832.  
  1833.                                 (* Create the process information window *)
  1834.                                 processInfoWindow := CreateProcessInfoWindow;
  1835.                                 IF processInfoWindow <> NIL THEN
  1836.                                     BEGIN
  1837.                                         (* Put handle to PSN into refCon *)
  1838.                                         psnHandle := NewHandleMargin (SIZEOF
  1839.                                                 (ProcessSerialNumber), kAllocApp,
  1840.                                                 NOT kAllocClr);
  1841.                                         IF psnHandle = NIL THEN
  1842.                                             HandleError (rMemErrMessages,
  1843.                                                     kMemErrProcInfoOpenMsg);
  1844.                                         BlockMove (Ptr(@processInfo.processNumber),
  1845.                                                 psnHandle^, SIZEOF (ProcessSerialNumber));
  1846.                                         SetWRefCon (processInfoWindow,
  1847.                                                 LongInt(psnHandle));
  1848.  
  1849.                                         (* Update dlog items to reflect proc info *)
  1850.                                         SetUpProcessInfoItems (processInfoWindow,
  1851.                                                 processInfo);
  1852.                                     END
  1853.                                 ELSE
  1854.                                     gotSelection := FALSE
  1855.                             END;
  1856.  
  1857.                         (* Go to the next cell *)
  1858.                         currCell.v := currCell.v + 1
  1859.                     END
  1860.             END
  1861.     END;
  1862.  
  1863.  
  1864. {$S ProcessGuts}
  1865. (*******************************************************************************
  1866. * Public: DoTerminateProcess
  1867. *
  1868. * The List Manager is used to get all of the selected processes in
  1869. * processListWindow.  The process serial number of each of these processes is
  1870. * extracted and is then used when calling TerminateProcess.
  1871. *******************************************************************************)
  1872.  
  1873.     PROCEDURE DoTerminateProcess (processListWindow: WindowPtr);
  1874.  
  1875.         CONST
  1876.             kFindNext = TRUE; {Pass to LGetSelect to find sequence of selections}
  1877.  
  1878.         VAR
  1879.             procList:     ListHandle;         {Handle to List Mgr process list}
  1880.             currCell:     Point;              {Cell that has selection}
  1881.             listInfo:     ProcessListInfoRec; {Process info from List Mgr list}
  1882.             listInfoLen:  Integer;            {Length of list info in bytes}
  1883.             gotSelection: Boolean;            {T if got sel’d cell, F if none}
  1884.             error:        OSErr;
  1885.  
  1886.         PROCEDURE HandleError (messageClass: Integer;
  1887.                                messageIndex: Integer);
  1888.  
  1889.             VAR
  1890.                 result: Integer; {Result of alert; ignored}
  1891.  
  1892.         BEGIN
  1893.             result := ShowStopAlert (messageClass, messageIndex);
  1894.             gError := noErr;
  1895.             EXIT (DoTerminateProcess)
  1896.         END;
  1897.  
  1898.     BEGIN
  1899.         (* Get the List Manager’s copy of the process list *)
  1900.         procList := ListHandle(GetWRefCon (processListWindow));
  1901.  
  1902.         (* Keep looping until all selected processes have been terminated *)
  1903.         currCell.v := 0;
  1904.         currCell.h := 0;
  1905.         gotSelection := TRUE;
  1906.         WHILE gotSelection DO
  1907.             BEGIN
  1908.                 gotSelection := LGetSelect (kFindNext, (*◊*)currCell, procList);
  1909.                 IF gotSelection THEN
  1910.                     BEGIN
  1911.                         listInfoLen := SIZEOF (ProcessListInfoRec);
  1912.                         LGetCell (Ptr(@listInfo), (*◊*)listInfoLen, currCell,
  1913.                                 procList);
  1914.  
  1915.                         (* Kill the specified process *)
  1916.                         error := TerminateProcess (listInfo.serialNumber);
  1917.                         IF error <> noErr THEN
  1918.                             HandleError (rMiscErrMessages, kMiscErrUnknownMsg);
  1919.  
  1920.                         (* Go to the next cell *)
  1921.                         currCell.v := currCell.v + 1
  1922.                     END
  1923.             END
  1924.     END;
  1925.